/** 基于栈内存分配的微型缓冲区，由于基于栈内存分配，所以不要分配过大的内存导致栈溢出
 * @file BufferWriter.hpp
 * @author Kiven Lee
 * @version 1.0
 * @date 2021-08-08
 *
 * @copyright Copyright Kivensoft (c) 2021
 *
 */
#pragma once

#include <cstddef>
#include <charconv>
#include <iterator>
#include <type_traits>

namespace klib {

    template<typename ARG, int _Num = 500>
    class buffer_writer_t {

    public:
        typedef void (*OnWrite) (void* arg, const char* data, size_t size);

        typedef char           value_type;
        typedef char *         iterator;
        typedef std::size_t    size_type;
        typedef std::ptrdiff_t difference_type;

    private:
        char    _buf[_Num];
        int     _pos;
        void *  _arg;
        OnWrite _onwrite;

    public:
        buffer_writer_t(ARG* arg, void (*fn) (ARG* arg, const char* data, size_t size)): _pos(0), _arg(arg), _onwrite(fn) {}

        /** 写入1个字符 */
        void write(char c) {
            if (_pos >= _Num) flush();
            _buf[_pos++] = c;
        }

        /** 连续吸入2个字符 */
        void write(char c, char c2) {
            if (_pos >= _Num - 1) flush();
            _buf[_pos++] = c;
            _buf[_pos++] = c2;
        }

        /** 连续写入3个字符 */
        void write(char c, char c2, char c3) {
            if (_pos >= _Num - 2) flush();
            _buf[_pos++] = c;
            _buf[_pos++] = c2;
            _buf[_pos++] = c3;
        }

        /** 写入src的内容
         *
         * @param src 要写入源内容的地址
         * @param len 写入长度
         */
        void write(const char* src, std::size_t len) {
            if (_pos >= _Num) flush();
            if (len > (std::size_t)(_Num - _pos)) {
                flush();
                _onwrite(_arg, src, len);
            } else {
                for (char *p = _buf + _pos, *pmax = p + len; p < pmax; ++p, ++src) *p = *src;
                _pos += len;
            }
        }

        /** 写入字符串 */
        void write(const char* src) { write(src, std::strlen(src)); }

        /** 写入字符串 */
        void write(const std::string_view& src) { write(src.data(), src.length()); }

        /** 写入字符串 */
        void write(const std::string& src) { write(src.data(), src.length()); }

        /** 写入bool/int/float数值类的值
         *
         * @tparam T        值类型: bool|char|[u]int(8|16|32|64)_t
         * @param  number   要写入的值
         * @param  radix    值的转换进制：2|8|10|16
         * @param  bufsize  为写入开辟的栈缓冲区大小
         * @return true: 转换成功, false: 转换失败
         */
        template<typename _Tp>
        std::enable_if_t<std::is_integral_v<_Tp>, bool>
        write_int(_Tp number, int radix = 10, int bufsize = 32) {
            char b[bufsize];
            auto r = std::to_chars(b, b + bufsize, number, radix);
            if (0 == static_cast<int>(r.ec)) write(b, r.ptr - b);
            return 0 == static_cast<int>(r.ec);
        }

        /** 刷新缓冲区，把缓冲区的内容写入 */
        void flush() {
            if (_pos > 0) {
                _onwrite(_arg, _buf, _pos);
                _pos = 0;
            }
        }

        /** 获取下一个写入位置 */
        std::size_t position() const { return _pos; }

        /** 设置下一个写入位置 */
        void position(std::size_t pos) { _pos = pos; }

        /** 获取缓冲区大小 */
        constexpr std::size_t size() const { return _Num; }

        /** 获取剩余可写入缓冲区大小 */
        std::size_t freespace() const { return _Num - _pos; }

        /** 运算符重载，支持随机写入, 无范围校验，请谨慎使用 */
        char& operator[](std::size_t idx) { return _buf[idx]; }

        /** back_inserter_iterator需要的函数 */
        void push_back(const char& v) { write(v); }

        // 禁止进行复制
        buffer_writer_t(const buffer_writer_t& src) = delete;
        buffer_writer_t& operator =(const buffer_writer_t& src) = delete;

        // 禁止在堆中分配对象
        void* operator new(size_t count)   = delete;
        void* operator new[](size_t count) = delete;
        void  operator delete(void* ptr)   = delete;
        void  operator delete[](void* ptr) = delete;
    };


    /** 基于缓存的IO类实现，可对输出或FILE*类型进行缓冲 */
    template <int _Num = 500>
    struct buffer_io_t : buffer_writer_t<std::FILE, _Num> {

    private:
        static inline void _BufferIO_OnWrite(void* arg, const char* data, size_t size) {
            std::fwrite(data, 1, size, (FILE*)arg);
        }

    public:
        buffer_io_t(std::FILE* out = stdout): buffer_writer_t<FILE, _Num>(out, _BufferIO_OnWrite) {}
    };

} // namespace klib
